(function () {
  // 没有assign方法添加
  if (typeof Object.assign != 'function') {
    (function () {
      Object.assign = function (target) {
        'use strict';
        if (target === undefined || target === null) {
          throw new TypeError('Cannot convert undefined or null to object');
        }

        var output = Object(target);
        for (var index = 1; index < arguments.length; index++) {
          var source = arguments[index];
          if (source !== undefined && source !== null) {
            for (var nextKey in source) {
              if (source.hasOwnProperty(nextKey)) {
                output[nextKey] = source[nextKey];
              }
            }
          }
        }
        return output;
      };
    })();
  }

  /**
   * 判断是否为js dom对象
   * 由于在Chrome,Opera中HTMLElement的类型为function,因此需要做浏览器兼容性判断
   * @param obj 目标对象
   * @returns {boolean} 为js dom对象返回true
   */
  function isDom(obj) {
    return (typeof HTMLElement === 'object')
            ? obj instanceof HTMLElement
            : obj && typeof obj === 'object' && obj.nodeType === 1 && typeof obj.nodeName === 'string';
  }

  /**
   *
   * @param ele 元素dom节点
   * @param $ css对象或字符串
   * @param _ 样式值
   * @returns {*}
   */
  function eleCss(ele, $, _) {
    if (!ele || !isDom(ele)) {
      return;
    }
    if (typeof $ === 'string') {
      if (_ === undefined) {
        return window.getComputedStyle(ele)[$];
      }
      ele.style[$] = _;
    } else if (typeof $ === 'object') {
      var keys = Object.keys($);
      for (var i = 0; i < keys.length; i++) {
        var key = keys[i];
        ele.style[key] = $[key];
      }
    }
    return ele;
  }

  function eleCssSize(ele, $) {
    return parseInt(eleCss(ele, $)) || 0;
  }

  function eleOffset(ele) {
    var totalLeft = null, totalTop = null, par = ele.offsetParent;
    //首先把自己本身的进行累加
    totalLeft += ele.offsetLeft;
    totalTop += ele.offsetTop;

    //只要没有找到body，我们就把父级参照物的边框和偏移量累加
    while (par) {
      if (navigator.userAgent.indexOf("MSIE 8.0") === -1) {
        //不是标准的ie8浏览器，才进行边框累加
        //累加父级参照物边框
        totalLeft += par.clientLeft;
        totalTop += par.clientTop;
      }
      //累加父级参照物本身的偏移
      totalLeft += par.offsetLeft;
      totalTop += par.offsetTop;
      par = par.offsetParent;
    }
    return {left: totalLeft, top: totalTop};
  }


  var currentDom;
  var currentTooltip;
  var timeout;

  /**
   * <p>使用方式 new Tips(options);</p>
   * <p>支持的options配置项：</p>
   *     <ol>
   *         <li>id: 目标</li>
   *         <li>width: 气泡框宽度</li>
   *         <li>height: 气泡框高度</li>
   *         <li>minWidth: 气泡框最小宽度</li>
   *         <li>minHeight: 气泡框最小高度</li>
   *         <li>maxWidth: 气泡框最大宽度，默认为dom宽度的两倍</li>
   *         <li>maxHeight: 气泡框最大高度</li>
   *         <li>color: 字体颜色，默认#000</li>
   *         <li>backgroundColor: 背景颜色，默认#fff</li>
   *         <li>position: 气泡框位置，可选值有auto|top|right|bottom|left，为auto时自动判断</li>
   *         <li>horizontalAlign: 水平对齐，可选值有center|left|right，仅在position为top和bottom有效</li>
   *         <li>verticalAlign: 垂直对齐，可选值有middle|top|bottom，仅在position为left和right有效</li>
   *         <li>content: 气泡框内容，string|function类型</li>
   *         <li>duration: 气泡框显示时长，单位ms，默认100s</li>
   *         <li>borderWidth: 气泡框边框大小，为0无边框，默认为0</li>
   *         <li>borderColor: 气泡框边框颜色，默认值#ccc,</li>
   *         <li>borderRadius: 气泡框圆角，默认5px</li>
   *     </ol>
   */
  function Tips(options) {
    var targetObj;
    var id = options.id;

    if (isDom(id)) {
      targetObj = [id];
    } else if (targetObj instanceof HTMLCollection || targetObj instanceof NodeList) {
      targetObj = id;
    } else if (id && typeof id === 'string') {
      targetObj = document.querySelectorAll(id);
    } else {
      throw new TypeError('The parameter id is required, and is either a string or javaScript dom object.');
    }

    if (!targetObj || targetObj.length === 0) {
      return;
    }

    var params = Object.assign({
      position: 'auto',
      horizontalAlign: 'center',
      verticalAlign: 'middle',
      content: "",
      duration: 100,
      borderWidth: 0,
      borderRadius: 5
    }, options);

    var mouseenter = function () {
      var that = this;

      window.clearTimeout(timeout);
      if (that === currentDom) {
        return;
      }

      {
        var tooltips = document.getElementsByClassName('tooltip');
        for (var i = 0; i < tooltips.length; i++) {
          tooltips.item(i).remove();
        }
      }

      var tooltip = document.createElement('div');
      tooltip.classList.add('tooltip');
      document.body.appendChild(tooltip);


      var content = params.content;
      if (typeof params.content === 'function') {
        content = params.content(params, that);
      }
      var tip = document.createElement('div');
      tip.classList.add('tips');
      tip.innerHTML = content;
      tooltip.appendChild(tip);

      var tipsTail = document.createElement('div');
      tipsTail.classList.add('tips-tail');
      tooltip.appendChild(tipsTail);

      var tipsTail2 = document.createElement('div');
      tipsTail2.classList.add('tips-tail2');
      tooltip.appendChild(tipsTail2);

      currentTooltip = tooltip;
      currentDom = that;

      var color = params.color || '#000';
      var backgroundColor = params.backgroundColor || '#fff';
      var borderWidth = isNaN(params.borderWidth) ? 0 : Number(params.borderWidth);
      var borderColor = params.borderColor || '#ccc';

      eleCss(tooltip, {
        width: params.width,
        height: params.height,
        minWidth: params.minWidth,
        minHeight: params.minHeight,
        maxWidth: params.maxWidth || (2 * that.offsetWidth + 'px'),
        maxHeight: params.maxHeight,
        borderRadius: params.borderRadius || '5px',
        position: 'absolute',
        overflow: 'visible',
        padding: borderWidth + 'px',
        backgroundColor: borderColor,
        zIndex: '99999'
      });

      eleCss(tip, {
        color: color,
        backgroundColor: backgroundColor,
        padding: '5px',
        borderRadius: params.borderRadius || '5px'
      });

      var gap = 5;
      var tipsCssObj = {
        position: 'absolute',
        width: '0px',
        height: '0px',
        border: (borderWidth + gap + 1) + 'px solid transparent'
      };

      eleCss(tipsTail, tipsCssObj);
      eleCss(tipsTail2, tipsCssObj);

      var position;
      var top;
      var left;

      var offset = eleOffset(that);
      var thatOuterWidth = that.offsetWidth;
      var thatOuterHeight = that.offsetHeight;

      var tooltipOuterWidth = tooltip.offsetWidth;
      var tooltipOuterHeight = tooltip.offsetHeight;

      // 自动计算气泡框弹出位置
      // 按右 - 上 - 左 - 下的顺序计算
      if (params.position === 'auto' || Array.isArray(params.position)) {
        var positionAutoOrderArr = params.position === 'auto' || params.position.length === 0
                ? ['right', 'top', 'left', 'bottom']
                : params.position;
        var windowWidth = document.body.clientWidth;
        var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;

        for (var i = 0; i < positionAutoOrderArr.length; i++) {
          var currentPosition = positionAutoOrderArr[i];
          if (currentPosition === 'right' && offset.left + thatOuterWidth + tooltipOuterWidth + gap < windowWidth) {
            position = 'right';
            // 到顶部的距离：offset.top - (document.documentElement.scrollTop || document.body.scrollTop)
          } else if (
                  currentPosition === 'top'
                  && offset.top - scrollTop - tooltipOuterHeight - gap > 0
                  && offset.left + tooltipOuterWidth < windowWidth
          ) {
            position = 'top';
          } else if (currentPosition === 'left' && offset.left - tooltipOuterWidth - gap > 0) {
            position = 'left';
          } else if (currentPosition === 'bottom') {
            position = 'bottom';
          }

          // 找到合适的定位退出for循环
          if (position) {
            break;
          }
        }
        // 没有找到合适的定位使用给定定位的第一种定位方式
        if (!position) {
          position = positionAutoOrderArr[0];
        }
      } else {
        position = params.position;
      }

      switch (position) {
        case 'top':
          top = offset.top - tooltipOuterHeight - gap - borderWidth;
          left = offset.left;
          tipsCssObj = {
            top: '100%',
            left: '50%',
            borderTopColor: borderColor,
            transform: 'translate(-50%, 0)'
          };
          break;
        case 'bottom':
          top = offset.top + thatOuterHeight + gap + borderWidth;
          left = offset.left;
          tipsCssObj = {
            top: '0%',
            left: '50%',
            borderBottomColor: borderColor,
            transform: 'translate(-50%, -100%)'
          };
          break;
        case 'left':
          top = offset.top;
          left = offset.left - tooltipOuterWidth - gap - borderWidth;
          tipsCssObj = {
            top: '50%',
            left: '100%',
            borderLeftColor: borderColor,
            transform: 'translate(0, -50%)'
          };
          break;
              // 其余都往右
        default:
          position = 'right';
          top = offset.top;
          left = offset.left + thatOuterWidth + gap + borderWidth;
          tipsCssObj = {
            top: '50%',
            left: 0,
            borderRightColor: borderColor,
            transform: 'translate(-100%, -50%)'
          };
      }


      if (position === 'top' || position === 'bottom') {
        if (params.horizontalAlign === 'left' || params.horizontalAlign === 'right') {
          var tipsLeft = thatOuterWidth / 2;
          if (params.horizontalAlign === 'right') {
            var leftOffset = thatOuterWidth - tooltipOuterWidth;
            left += leftOffset;
            tipsLeft -= leftOffset;
          }
          // 当气泡框宽度大于目标dom宽度时，取目标dom宽度的一半作为气泡脚水平位置
          if (tooltipOuterWidth > thatOuterWidth) {
            tipsCssObj['left'] = tipsLeft + 'px';
          }
        } else {
          // 气泡框水平居中
          left += (thatOuterWidth - tooltipOuterWidth) / 2;
        }
      } else {
        if (params.verticalAlign === 'top' || params.verticalAlign === 'bottom') {
          var tipsTop = thatOuterHeight / 2;
          if (params.verticalAlign === 'bottom') {
            var topOffset = thatOuterHeight - tooltipOuterHeight;
            top += topOffset;
            tipsTop -= topOffset;
          }

          // 当气泡框宽度大于目标dom宽度时，取目标dom高度的一半作为气泡脚垂直位置
          if (tooltipOuterHeight > thatOuterHeight) {
            tipsCssObj['top'] = tipsTop + 'px';
          }
        } else {
          top += (thatOuterHeight - tooltipOuterHeight) / 2;
        }
      }

      eleCss(tooltip, {
        top: top + 'px',
        left: left + 'px'
      });
      eleCss(tipsTail, tipsCssObj);
      tipsCssObj['border' + position.charAt(0).toUpperCase() + position.substring(1) + 'Color'] = backgroundColor;
      eleCss(tipsTail2, tipsCssObj);

      if (borderWidth > 0) {
        eleCss(tipsTail2, 'borderWidth', eleCssSize(tipsTail2, 'borderWidth') - 1 + 'px');

        var tipsTailTop = eleCssSize(tipsTail, 'top');
        var tipsTailLeft = eleCssSize(tipsTail, 'left');
        var tipsTail2Top = eleCssSize(tipsTail2, 'top');
        var tipsTail2Left = eleCssSize(tipsTail2, 'left');

        switch (position) {
          case 'top':
            eleCss(tipsTail2, 'top', tipsTail2Top - borderWidth - 1 + 'px');
            break;
          case 'bottom':
            eleCss(tipsTail, 'top', tipsTailTop + 1 + 'px');
            eleCss(tipsTail2, 'top', tipsTail2Top + borderWidth + 1 + 'px');
            break;
          case 'left':
            eleCss(tipsTail, 'left', tipsTailLeft - 1 + 'px');
            eleCss(tipsTail2, 'left', tipsTail2Left - borderWidth - 1 + 'px');
            break;
          default:
            eleCss(tipsTail, 'left', tipsTailLeft + 1 + 'px');
            eleCss(tipsTail2, 'left', tipsTail2Left + borderWidth + 1 + 'px');
        }
      }

      // 当气泡框超出显示屏时
      if (tooltipOuterHeight !== tooltip.offsetHeight) {
        if (['top', 'left', 'right'].indexOf(position) !== -1) {
          var tooltipHeightChangeGap = tooltip.offsetHeight - tooltipOuterHeight;
          var tooltipOffsetTop = eleOffset(tooltip).top;

          var offsetTop;
          if (position === 'top') {
            offsetTop = tooltipOffsetTop - tooltipHeightChangeGap;
          } else {
            offsetTop = tooltipOffsetTop - tooltipHeightChangeGap / 2;
          }
          eleCss(tooltip, {top: offsetTop + 'px'});
        }
      }

      tooltip.addEventListener('mouseenter', function () {
        window.clearTimeout(timeout);
      });
      tooltip.addEventListener('mouseleave', function () {
        timeout = window.setTimeout(function () {
          if (that === currentDom) {
            currentTooltip.remove();
            currentDom = undefined;
          }
        }, params.duration);
      });
    }
    var mouseleave = function () {
      var that = this;
      timeout = window.setTimeout(function () {
        if (that === currentDom) {
          currentTooltip.remove();
          currentDom = undefined;
        }
      }, params.duration);
    }

    for (var i = 0; i < targetObj.length; i++) {
      var element = targetObj[i];
      element.addEventListener('mouseenter', mouseenter);
      element.addEventListener('mouseleave', mouseleave);
    }
  }

  window.Tips = Tips;
})();